
#include "stdafx.h"
#include "sgllib.h"
#include "sgldef.h"
#include "sgldecode.h"

static inline SGL_COLOR AlphaBlend(BYTE byAlpha, SGL_COLOR sSrcColor, SGL_COLOR sDestColor)
{
	if (!byAlpha)
		return sDestColor;

	if (byAlpha == SGL_AMAX)
		return sSrcColor;

	WORD &wSrcColor = sSrcColor.wColor;
	WORD &wDestColor = sDestColor.wColor;

	INT nRed = (SGL_RVALUE(wSrcColor) * byAlpha * SGL_RMAX / SGL_AMAX + SGL_RVALUE(wDestColor) * (SGL_RMAX - (byAlpha * SGL_RMAX / SGL_AMAX))) / SGL_RMAX;
	INT nGreen = (SGL_GVALUE(wSrcColor) * byAlpha * SGL_GMAX / SGL_AMAX + SGL_GVALUE(wDestColor) * (SGL_GMAX - (byAlpha * SGL_GMAX / SGL_AMAX))) / SGL_GMAX;
	INT nBlue = (SGL_BVALUE(wSrcColor) * byAlpha * SGL_BMAX / SGL_AMAX + SGL_BVALUE(wDestColor) * (SGL_BMAX - (byAlpha * SGL_BMAX / SGL_AMAX))) / SGL_BMAX;

	SGL_COLOR sColor;
	sColor.wColor = SGL_MAKECOLOR(nRed, nGreen, nBlue);
	return sColor;
}

SglDecode::SglDecode() : m_Image(NULL)
{

}

SglDecode::~SglDecode()
{
	FreeImage();
}

const SGL_COLOR *SglDecode::GetImage(VOID) const
{
	return m_Image;
}

VOID SglDecode::FreeImage(VOID)
{
	delete [] m_Image;
	m_Image = NULL;
}

BOOL SglDecode::DecodeEmpty(UINT nWidth, UINT nHeight, UINT nPitch, SGL_COLOR sBgColor)
{
	ASSERT(!m_Image);

	if (!nWidth || !nHeight || !nPitch)
		return FALSE;

	if ((nPitch & 0x1) || nPitch < (nWidth >> 1))
		return FALSE;

	m_Image = new SGL_COLOR[nHeight * nPitch >> 1];
	if (!m_Image)
		return FALSE;

	DecodeEmpty(nWidth, nHeight, m_Image, nPitch, sBgColor);
	return TRUE;
}

BOOL SglDecode::DecodeDXT1(const SGL_DXT1 *pData, UINT nWidth, UINT nHeight, UINT nPitch, SGL_COLOR sBgColor)
{
	ASSERT(!m_Image);

	if (!pData || !nWidth || !nHeight || !nPitch)
		return FALSE;

	if ((nPitch & 0x1) || nPitch < (nWidth >> 1))
		return FALSE;

	m_Image = new SGL_COLOR[nHeight * nPitch >> 1];
	if (!m_Image)
		return FALSE;

	DecodeDXT1(pData, nWidth, nHeight, m_Image, nPitch, sBgColor);
	return TRUE;
}

BOOL SglDecode::DecodeDXT3(const SGL_DXT3 *pData, UINT nWidth, UINT nHeight, UINT nPitch, SGL_COLOR sBgColor)
{
	ASSERT(!m_Image);

	if (!pData || !nWidth || !nHeight || !nPitch)
		return FALSE;

	if ((nPitch & 0x1) || nPitch < (nWidth >> 1))
		return FALSE;

	m_Image = new SGL_COLOR[nHeight * nPitch >> 1];
	if (!m_Image)
		return FALSE;

	DecodeDXT3(pData, nWidth, nHeight, m_Image, nPitch, sBgColor);
	return TRUE;
}

BOOL SglDecode::DecodeEmptyAlpha(SGL_COLOR *pImage, UINT nWidth, UINT nHeight, UINT nPitch, SGL_COLOR sBgColor)
{
	if (!pImage || !nWidth || !nHeight || !nPitch)
		return FALSE;

	if ((nPitch & 0x1) || nPitch < (nWidth << 1))
		return FALSE;

	for (UINT j = 0U; j < nHeight; j++)
	{
		SGL_COLOR *pDest = pImage + (nPitch >> 1) * j;

		for (UINT i = 0U; i < nWidth; i++)
		{
			*pDest = sBgColor;
			pDest++;
		}
	}

	return TRUE;
}

BOOL SglDecode::DecodeDXT1Alpha(SGL_COLOR *pImage, UINT nWidth, UINT nHeight, UINT nPitch, const SGL_DXT1 *pData, SGL_COLOR sBgColor)
{
	if (!pImage || !nWidth || !nHeight || !nPitch || !pData)
		return FALSE;

	if ((nWidth & 0x3) || (nHeight & 0x3) || nPitch < (nWidth >> 1))
		return FALSE;

	const SGL_DXT1 *pSource = pData;

	for (UINT y = 0U; y < nHeight; y += 4U)
	{
		for (UINT x = 0U; x < nWidth; x += 4U)
		{
			if (pSource->uColor0.wColor <= pSource->uColor1.wColor) 
			{
				SGL_COLOR *pDest = pImage + (nPitch >> 1) * y + x;
				DWORD dwPixels = pSource->dwPixels;

				for (INT j = 0; j < 4; j++)
				{
					if ((dwPixels & 0x03) == 0x03)
						pDest[0] = sBgColor;

					if ((dwPixels & 0x0c) == 0x0c)
						pDest[1] = sBgColor;

					if ((dwPixels & 0x30) == 0x30)
						pDest[2] = sBgColor;

					if ((dwPixels & 0xc0) == 0xc0)
						pDest[3] = sBgColor;

					pDest += nPitch >> 1;
					dwPixels >>= 8;
				}
			}

			pSource++;
		}
	}

	return TRUE;
}

BOOL SglDecode::DecodeDXT3Alpha(SGL_COLOR *pImage, UINT nWidth, UINT nHeight, UINT nPitch, const SGL_DXT3 *pData, SGL_COLOR sBgColor)
{
	if (!pImage || !nWidth || !nHeight || !nPitch || !pData)
		return FALSE;

	if ((nWidth & 0x3) || (nHeight & 0x3) || nPitch < (nWidth >> 1))
		return FALSE;

	const SGL_DXT3 *pSource = pData;

	for (UINT y = 0U; y < nHeight; y += 4U)
	{
		for (UINT x = 0U; x < nWidth; x += 4U)
		{
			SGL_COLOR *pDest = pImage + (nPitch >> 1) * y + x;

			for (INT j = 0; j < 4; j++)
			{
				WORD wAlpha = pSource->aAlpha[j];

				for (INT i = 0; i < 4; i++)
				{
					pDest[i] = ::AlphaBlend(wAlpha & 0x0f, pDest[i], sBgColor);
					wAlpha >>= 4;
				}

				pDest += nPitch >> 1;
			}

			pSource++;
		}
	}

	return TRUE;
}

BOOL SglDecode::AlphaBlend(LPCVOID pAlpha, UINT nWidth, UINT nHeight, SGL_COLOR sBgColor, LPVOID pData, UINT nPitch)
{
	if (!pAlpha || !pData || !nPitch)
		return FALSE;

	if ((nPitch & 0x1) || nPitch < (nWidth >> 1))
		return FALSE;

	const BYTE *pSource = static_cast<const BYTE *>(pAlpha);
	SGL_COLOR *pLine = static_cast<SGL_COLOR *>(pData);

	for (UINT j = 0U; j < nHeight; j++)
	{
		SGL_COLOR *pColor = pLine;

		for (UINT i = 0U; i < nWidth; i += 2U, pSource++)
		{
			*pColor++ = ::AlphaBlend(*pSource & 0x0f, *pColor, sBgColor);
			*pColor++ = ::AlphaBlend(*pSource >> 4, *pColor, sBgColor);
		}

		pLine += nPitch >> 1;
	}

	return TRUE;
}

BOOL SglDecode::GetEmptyAlpha(UINT nWidth, UINT nHeight, LPVOID pBuffer, UINT nPitch)
{
	if (!nWidth || !nHeight || !pBuffer || !nPitch)
		return FALSE;

	if ((nWidth & 0x1) || nPitch < (nWidth >> 1))
		return FALSE;

	LPBYTE pDest = static_cast<LPBYTE>(pBuffer);

	for (UINT i = 0U; i < nHeight; i++, pDest += nPitch)
		memset(pDest, 0, nWidth >> 1);

	return TRUE;
}

BOOL SglDecode::GetDXT1Alpha(const SGL_DXT1 *pData, UINT nWidth, UINT nHeight, LPVOID pBuffer, UINT nPitch)
{
	if (!pData || !nWidth || !nHeight || !pBuffer || !nPitch)
		return FALSE;

	if ((nWidth & 0x3) || (nHeight & 0x3) || nPitch < (nWidth >> 1))
		return FALSE;

	const SGL_DXT1 *pSource = pData;
	LPWORD pDest = static_cast<LPWORD>(pBuffer);

	for (UINT y = 0U; y < nHeight; y += 4U)
	{
		for (UINT x = 0U; x < nWidth; x += 4U)
		{
			LPWORD pLine = pDest + (x >> 2);
			DWORD dwPixels = pSource->dwPixels;

			for (INT j = 0; j < 4; j++)
			{
				*pLine = 0xffff;

				if (pSource->uColor0.wColor <= pSource->uColor1.wColor)
				{
					if ((dwPixels & 0x03) == 0x03)
						*pLine &= 0xfff0;

					if ((dwPixels & 0x0c) == 0x0c)
						*pLine &= 0xff0f;

					if ((dwPixels & 0x30) == 0x30)
						*pLine &= 0xf0ff;

					if ((dwPixels & 0xc0) == 0xc0)
						*pLine &= 0x0fff;

					dwPixels >>= 8;
				}

				pLine += nPitch >> 1;
			}

			pSource++;
		}

		pDest += nPitch << 1;
	}

	return TRUE;
}

BOOL SglDecode::GetDXT3Alpha(const SGL_DXT3 *pData, UINT nWidth, UINT nHeight, LPVOID pBuffer, UINT nPitch)
{
	if (!pData || !nWidth || !nHeight || !pBuffer || !nPitch)
		return FALSE;

	if ((nWidth & 0x3) || (nHeight & 0x3) || nPitch < (nWidth >> 1))
		return FALSE;

	const SGL_DXT3 *pSource = pData;
	LPWORD pDest = static_cast<LPWORD>(pBuffer);

	for (UINT y = 0U; y < nHeight; y += 4U)
	{
		for (UINT x = 0U; x < nWidth; x += 4U)
		{
			LPWORD pLine = pDest + (x >> 2);

			for (INT i = 0; i < 4; i++)
			{
				*pLine = pSource->aAlpha[i];
				pLine += nPitch >> 1;
			}

			pSource++;
		}

		pDest += nPitch << 1;
	}

	return TRUE;
}

VOID SglDecode::DecodeEmpty(UINT nWidth, UINT nHeight, SGL_COLOR *pBuffer, UINT nPitch, SGL_COLOR sBgColor)
{
	ASSERT(nWidth && nHeight && pBuffer && nPitch);

	SGL_COLOR *pLine = pBuffer;

	for (UINT j = 0U; j < nHeight; j++)
	{
		SGL_COLOR *pDest = pLine;

		for (UINT i = 0U; i < nWidth; i++)
			*pDest++ = sBgColor;

		pLine += nPitch >> 1;
	}
}

VOID SglDecode::DecodeDXT1(const SGL_DXT1 *pData, UINT nWidth, UINT nHeight, SGL_COLOR *pBuffer, UINT nPitch, SGL_COLOR sBgColor)
{
	ASSERT(pData && nWidth && nHeight && pBuffer && nPitch);

	const SGL_DXT1 *pSource = pData;

	for (UINT y = 0U; y < nHeight; y += 4U)
	{
		for (UINT x = 0U; x < nWidth; x += 4U)
		{
			SGL_COLOR aColor[4];
			aColor[0] = pSource->uColor0;
			aColor[1] = pSource->uColor1;
			DWORD dwPixels = pSource->dwPixels;

			if (aColor[0].wColor > aColor[1].wColor) 
			{
				aColor[2].sColor.wRed = (2 * aColor[0].sColor.wRed + aColor[1].sColor.wRed) / 3;
				aColor[2].sColor.wGreen = (2 * aColor[0].sColor.wGreen + aColor[1].sColor.wGreen) / 3;
				aColor[2].sColor.wBlue = (2 * aColor[0].sColor.wBlue + aColor[1].sColor.wBlue) / 3;
				aColor[3].sColor.wRed = (aColor[0].sColor.wRed + 2 * aColor[1].sColor.wRed) / 3;
				aColor[3].sColor.wGreen = (aColor[0].sColor.wGreen + 2 * aColor[1].sColor.wGreen) / 3;
				aColor[3].sColor.wBlue = (aColor[0].sColor.wBlue + 2 * aColor[1].sColor.wBlue) / 3;
			}
			else
			{
				aColor[2].sColor.wRed = (aColor[0].sColor.wRed + aColor[1].sColor.wRed) / 2;
				aColor[2].sColor.wGreen = (aColor[0].sColor.wGreen + aColor[1].sColor.wGreen) / 2;
				aColor[2].sColor.wBlue = (aColor[0].sColor.wBlue + aColor[1].sColor.wBlue) / 2;
				aColor[3] = sBgColor;
			}

			SGL_COLOR *pDest = pBuffer + (nPitch >> 1) * y + x;

			for (INT j = 0; j < 4; j++)
			{
				for (INT i = 0; i < 4; i++)
				{
					pDest[i] = aColor[dwPixels & 0x3];
					dwPixels >>= 2;
				}

				pDest += nPitch >> 1;
			}

			pSource++;
		}
	}
}

VOID SglDecode::DecodeDXT3(const SGL_DXT3 *pData, UINT nWidth, UINT nHeight, SGL_COLOR *pBuffer, UINT nPitch, SGL_COLOR sBgColor)
{
	ASSERT(pData && nWidth && nHeight && pBuffer && nPitch);

	const SGL_DXT3 *pSource = pData;

	for (UINT y = 0U; y < nHeight; y += 4U)
	{
		for (UINT x = 0U; x < nWidth; x += 4U)
		{
			SGL_COLOR aColor[4];
			aColor[0] = pSource->uColor0;
			aColor[1] = pSource->uColor1;
			DWORD dwPixels = pSource->dwPixels;

			aColor[2].sColor.wRed = (2 * aColor[0].sColor.wRed + aColor[1].sColor.wRed) / 3;
			aColor[2].sColor.wGreen = (2 * aColor[0].sColor.wGreen + aColor[1].sColor.wGreen) / 3;
			aColor[2].sColor.wBlue = (2 * aColor[0].sColor.wBlue + aColor[1].sColor.wBlue) / 3;
			aColor[3].sColor.wRed = (aColor[0].sColor.wRed + 2 * aColor[1].sColor.wRed) / 3;
			aColor[3].sColor.wGreen = (aColor[0].sColor.wGreen + 2 * aColor[1].sColor.wGreen) / 3;
			aColor[3].sColor.wBlue = (aColor[0].sColor.wBlue + 2 * aColor[1].sColor.wBlue) / 3;

			SGL_COLOR *pDest = pBuffer + (nPitch >> 1) * y + x;

			for (INT j = 0; j < 4; j++)
			{
				WORD wAlpha = pSource->aAlpha[j];

				for (INT i = 0; i < 4; i++)
				{
					pDest[i] = ::AlphaBlend(wAlpha & 0x0f, aColor[dwPixels & 0x3], sBgColor);
					dwPixels >>= 2;
					wAlpha >>= 4;
				}

				pDest += nPitch >> 1;
			}

			pSource++;
		}
	}
}
